/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.prefs.Preferences;

import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.RootPaneContainer;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.plaf.basic.BasicTextAreaUI;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.plaf.basic.BasicTextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;

import org.isqlviewer.bookmarks.Bookmark;
import org.isqlviewer.bookmarks.BookmarkFolder;
import org.isqlviewer.bookmarks.ColorLabel;
import org.isqlviewer.swing.SwingUtilities;
import org.isqlviewer.swing.WizardPanel;
import org.isqlviewer.swing.action.SwingEventManager;
import org.isqlviewer.ui.laf.ColorLedIcon;
import org.isqlviewer.util.LocalMessages;

/**
 * Editor UI for modifying and viewing bookmark information.
 * <p>
 * 
 * @author Markus A. Kobold &lt;mkobold at sprintpcs dot com&gt;
 * @version 1.0
 */
public class BookmarkEditor extends AbstractObjectEditor implements DocumentListener, UndoableEditListener,
        ActionListener {

    private static final int ACTION_OK = 0;
    private static final int ACTION_APPLY = 1;
    private static final int ACTION_CANCEL = 2;

    private LocalMessages messages = new LocalMessages("org.isqlviewer.ui.ResourceBundle");

    private Bookmark bookmark = null;
    private SqlCommandEditor sqlEditor = new SqlCommandEditor();
    private Document nameDocument = null;
    private Document createdDocument = null;
    private Document locationDocument = null;
    private Document lastUsedDocument = null;
    private Document useCountDocument = null;
    private UndoManager undoManager = new UndoManager();
    private ButtonGroup colorLabelSelection = new ButtonGroup();

    private RootPaneContainer frameReference = null;
    private OperationStatus status = OperationStatus.NOT_MODIFIED;

    public void undoableEditHappened(UndoableEditEvent e) {

        if (e == null || e.getEdit() == null) {
            return;
        }

        UndoableEdit edit = e.getEdit();
        String name = edit.getPresentationName();
        if (name != null && edit.canUndo()) {
            if (name.indexOf("style") < 0) {
                undoManager.addEdit(e.getEdit());
            } else {
                edit.die();
            }
        } else {
            edit.die();
        }
    }

    public void actionPerformed(ActionEvent event) {

        int action = -1;
        try {
            action = Integer.parseInt(event.getActionCommand());
        } catch (NumberFormatException ignored) {
            // this will happen when the color label is changed since the action command is the color name and not a
            // number //
        }
        Component component = (Component) event.getSource();
        Window window = javax.swing.SwingUtilities.getWindowAncestor(component);

        switch (action) {
            case -1 :
                SwingUtilities.setFrameModified(frameReference, true);
                break;
            case ACTION_APPLY :
                apply();
                break;
            case ACTION_OK :
                if (SwingUtilities.isFrameModified(frameReference).booleanValue()) {
                    apply();
                }
                window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
                break;
            case ACTION_CANCEL :
                int result = JOptionPane.NO_OPTION;
                if (SwingUtilities.isFrameModified(frameReference).booleanValue()) {
                    String msg = messages.getMessage("bookmarkeditor.cancel_warning");
                    result = JOptionPane.showConfirmDialog(window, msg, "", JOptionPane.YES_NO_OPTION);
                }
                if (status == OperationStatus.NOT_MODIFIED || result == JOptionPane.YES_OPTION) {
                    // if apply was called previously status will be MODIFIED //
                    if (status == OperationStatus.NOT_MODIFIED) {
                        status = OperationStatus.CANCELLED;
                    }
                    window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
                }
                break;
        }
    }

    public void changedUpdate(DocumentEvent e) {

    }

    public void insertUpdate(DocumentEvent e) {

        SwingUtilities.setFrameModified(frameReference, true);
        if (e.getDocument() == nameDocument) {
            precheckBookmarkName();
        }
    }

    public void removeUpdate(DocumentEvent e) {

        SwingUtilities.setFrameModified(frameReference, true);
        if (e.getDocument() == nameDocument) {
            precheckBookmarkName();
        }
    }

    @Override
    public OperationStatus getStatus() {

        return status;
    }

    @Override
    public void initializeView(Object object) {

        if (object instanceof Bookmark) {
            this.bookmark = (Bookmark) object;
        }
        initializeView();
    }

    public void configureMenubar(JMenuBar menuBar) {

        sqlEditor.configureMenubar(menuBar);
    }

    public void disposeView(Preferences preferences) {

        sqlEditor.disposeView(preferences);
    }

    public void initializeView() {

        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
        NumberFormat nf = NumberFormat.getIntegerInstance();

        sqlEditor.setText(bookmark.getCommandText());
        SwingUtilities.replaceDocumentContent(nameDocument, bookmark.getName());
        Date date = bookmark.getCreationTime();
        SwingUtilities.replaceDocumentContent(createdDocument, df.format(date));
        SwingUtilities.replaceDocumentContent(locationDocument, bookmark.getPath());
        date = bookmark.getLastAccess();
        date = (date == null) ? new Date() : date;
        SwingUtilities.replaceDocumentContent(lastUsedDocument, df.format(date));
        SwingUtilities.replaceDocumentContent(useCountDocument, nf.format(bookmark.getUseCount()));

        nameDocument.addUndoableEditListener(this);
        nameDocument.addDocumentListener(this);

        sqlEditor.addDocumentListener(this);
        sqlEditor.initializeView();

        Enumeration<AbstractButton> buttons = colorLabelSelection.getElements();
        while (buttons.hasMoreElements()) {
            AbstractButton colorButton = buttons.nextElement();
            Object property = colorButton.getClientProperty("COLOR_LABEL");
            if (property instanceof ColorLabel) {
                ColorLabel label = (ColorLabel) property;
                if (label == bookmark.getColorLabel()) {
                    colorLabelSelection.setSelected(colorButton.getModel(), true);
                    break;
                }
            } else if (bookmark.getColorLabel() == null) {
                colorLabelSelection.setSelected(colorButton.getModel(), true);
                break;
            }

        }
        SwingUtilities.setFrameModified(frameReference, false);
    }

    public void doLayout(JComponent parentComponent, Preferences preferences, SwingEventManager eventManager) {

        frameReference = (RootPaneContainer) javax.swing.SwingUtilities.getAncestorOfClass(RootPaneContainer.class,
                parentComponent);
        parentComponent.setLayout(new WizardPanel.Layout());
        JComponent rootPanel = new JPanel(new BorderLayout());
        JComponent commandPanel = new JPanel(new GridBagLayout());
        JTabbedPane editorLayout = new JTabbedPane(SwingConstants.TOP);

        Object constraint = null;
        JComponent component = null;
        Icon icon = null;
        String title = null;
        String tip = null;

        component = new JPanel(new GridBagLayout());
        layoutBasicEditor(preferences, eventManager, component);
        title = messages.getMessage("bookmarkeditor.general_tab.title");
        icon = SwingUtilities.loadIconResource("bookmark", 16);
        tip = messages.getMessage("bookmarkeditor.general_tab.tip");
        editorLayout.addTab(title, icon, component, tip);

        component = new JPanel(new GridBagLayout());
        layoutInformationView(component);
        title = messages.getMessage("bookmarkeditor.info_tab.title");
        icon = SwingUtilities.loadIconResource("information", 16);
        tip = messages.getMessage("bookmarkeditor.info_tab.tip");
        editorLayout.addTab(title, icon, component, tip);

        JButton actionButton = null;
        constraint = constrain(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        commandPanel.add(Box.createHorizontalGlue(), constraint);

        actionButton = new JButton(messages.getMessage("bookmarkeditor.okbutton.title"));
        actionButton.addActionListener(this);
        actionButton.setActionCommand(Integer.toString(ACTION_OK));
        actionButton.setToolTipText(messages.getMessage("bookmarkeditor.okbutton.tip"));
        constraint = constrain(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE);
        commandPanel.add(actionButton, constraint);

        actionButton = new JButton(messages.getMessage("bookmarkeditor.applybutton.title"));
        actionButton.addActionListener(this);
        actionButton.setActionCommand(Integer.toString(ACTION_APPLY));
        actionButton.setToolTipText(messages.getMessage("servicemanager.applybutton.tip"));
        constraint = constrain(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE);
        commandPanel.add(actionButton, constraint);

        actionButton = new JButton(messages.getMessage("bookmarkeditor.cancelbutton.title"));
        actionButton.addActionListener(this);
        actionButton.setActionCommand(Integer.toString(ACTION_CANCEL));
        actionButton.setToolTipText(messages.getMessage("bookmarkeditor.cancelbutton.tip"));
        constraint = constrain(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE);
        commandPanel.add(actionButton, constraint);

        rootPanel.add(editorLayout, BorderLayout.CENTER);
        rootPanel.add(commandPanel, BorderLayout.SOUTH);
        parentComponent.add(rootPanel);

        rootPanel.setPreferredSize(new Dimension(512, 360));
    }

    private void apply() {

        bookmark.setCommandText(sqlEditor.getText());
        try {
            bookmark.setName(nameDocument.getText(0, nameDocument.getLength()));
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        String colorLabel = colorLabelSelection.getSelection().getActionCommand();
        if (colorLabel == null) {
            bookmark.setColorLabel(null);
        } else {
            bookmark.setColorLabel(ColorLabel.valueOf(colorLabel));
        }

        SwingUtilities.setFrameModified(frameReference, false);
        status = OperationStatus.MODIFIED;
    }

    private void precheckBookmarkName() {

        try {
            String text = nameDocument.getText(0, nameDocument.getLength());
            BookmarkFolder parentFolder = bookmark.getFolder();
            JTextComponent textArea = (JTextComponent) nameDocument.getProperty("JCOMPONENT");
            if (parentFolder.containsBookmark(text)) {
                textArea.setForeground(UIManager.getColor("ToolTip.foreground"));
                textArea.setBackground(UIManager.getColor("ToolTip.background"));
            } else {
                String prefix = null;
                BasicTextUI uiClass = (BasicTextUI) textArea.getUI();
                // Why they didn't make the property prefix public in the UI is beyond me >.>
                if (uiClass instanceof BasicTextAreaUI) {
                    prefix = "TextArea";
                } else if (uiClass instanceof BasicTextFieldUI) {
                    prefix = "TextField";
                } else if (uiClass instanceof BasicEditorPaneUI) {
                    prefix = "EditorPane";
                }
                if (prefix == null) {
                    textArea.setForeground(Color.BLACK);
                    textArea.setBackground(Color.WHITE);
                } else {
                    textArea.setForeground(UIManager.getColor(prefix + ".foreground"));
                    textArea.setBackground(UIManager.getColor(prefix + ".background"));
                }
            }

        } catch (BadLocationException ignored) {
        }
    }

    private void layoutBasicEditor(Preferences preferences, SwingEventManager eventManager, JComponent editorSheet) {

        Object constraint = null;
        JComponent component = null;
        JLabel label = null;
        String title = null;
        String tip = null;

        title = messages.format("bookmarkeditor.bookmark-name.title");
        tip = messages.format("bookmarkeditor.bookmark-name.tip");
        component = new JTextField();
        nameDocument = ((JTextField) component).getDocument();
        nameDocument.putProperty("JCOMPONENT", component);

        localizeTextComponent(((JTextField) component), undoManager);
        component.setToolTipText(tip);
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 0, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        editorSheet.add(component, constraint);

        title = messages.format("bookmarkeditor.bookmark-sql.title");
        tip = messages.format("bookmarkeditor.bookmark-sql.tip");
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 2, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(0, 3, 2, 1, 1.0d, 1.0d, GridBagConstraints.WEST, GridBagConstraints.BOTH);
        component = new JPanel();
        editorSheet.add(component, constraint);

        sqlEditor.doLayout(component, preferences, eventManager);

    }

    private void layoutInformationView(JComponent editorSheet) {

        Object constraint = null;
        JComponent component = null;
        JLabel label = null;
        String title = null;
        String tip = null;
        ColorLabel[] colorLabels = ColorLabel.values();
        int labelCount = colorLabels.length + 2;

        title = messages.format("bookmarkeditor.path.title");
        tip = messages.format("bookmarkeditor.path.tip");
        component = new JTextField();
        ((JTextField) component).setEditable(false);
        localizeTextComponent(((JTextField) component), null);
        locationDocument = ((JTextField) component).getDocument();
        component.setFont(component.getFont().deriveFont(Font.BOLD));
        component.setToolTipText(tip);
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 0, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(1, 0, labelCount, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        editorSheet.add(component, constraint);

        title = messages.format("bookmarkeditor.created-date.title");
        tip = messages.format("bookmarkeditor.created-date.tip");
        component = new JTextField();
        ((JTextField) component).setEditable(false);
        localizeTextComponent(((JTextField) component), null);
        createdDocument = ((JTextField) component).getDocument();
        component.setFont(component.getFont().deriveFont(Font.BOLD));
        component.setToolTipText(tip);
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 1, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(1, 1, labelCount, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        editorSheet.add(component, constraint);

        title = messages.format("bookmarkeditor.lastused-date.title");
        tip = messages.format("bookmarkeditor.lastused-date.tip");
        component = new JTextField();
        ((JTextField) component).setEditable(false);
        localizeTextComponent(((JTextField) component), null);
        lastUsedDocument = ((JTextField) component).getDocument();
        component.setFont(component.getFont().deriveFont(Font.BOLD));
        component.setToolTipText(tip);
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 2, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(1, 2, labelCount, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        editorSheet.add(component, constraint);

        title = messages.format("bookmarkeditor.use-count.title");
        tip = messages.format("bookmarkeditor.use-count.tip");
        component = new JTextField();
        ((JTextField) component).setEditable(false);
        localizeTextComponent(((JTextField) component), null);
        useCountDocument = ((JTextField) component).getDocument();
        component.setFont(component.getFont().deriveFont(Font.BOLD));
        component.setToolTipText(tip);
        label = new JLabel(title);
        label.setLabelFor(component);
        constraint = constrain(0, 3, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);
        constraint = constrain(1, 3, labelCount, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL);
        editorSheet.add(component, constraint);

        constraint = constrain(0, 4, labelCount, 1, 0.0d, 0.0d, GridBagConstraints.CENTER, GridBagConstraints.NONE);
        editorSheet.add(Box.createVerticalStrut(12), constraint);

        title = messages.format("bookmarkeditor.color-label.title");
        tip = messages.format("bookmarkeditor.color-label.tip");
        label = new JLabel(title);
        label.setToolTipText(tip);
        constraint = constrain(0, 5, 1, 1, 0.0d, 0.0d, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(label, constraint);

        JToggleButton toggleButton = null;

        toggleButton = new JToggleButton();
        toggleButton.setIcon(SwingUtilities.loadIconResource("close_view", 16));
        toggleButton.addActionListener(this);
        toggleButton.setFocusPainted(true);
        toggleButton.setBorderPainted(false);
        toggleButton.setSelected(false);
        toggleButton.setMargin(new Insets(2, 2, 2, 2));
        colorLabelSelection.add(toggleButton);
        constraint = constrain(1, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE);
        editorSheet.add(toggleButton, constraint);

        for (int i = 0; i < colorLabels.length; i++) {
            toggleButton = new JToggleButton();
            toggleButton.setIcon(new ColorLedIcon(colorLabels[i].toColor(), 2, 2));
            toggleButton.addActionListener(this);
            toggleButton.setBorderPainted(false);
            toggleButton.setFocusPainted(true);
            toggleButton.setSelected(false);
            toggleButton.setMargin(new Insets(2, 2, 2, 2));
            toggleButton.putClientProperty("COLOR_LABEL", colorLabels[i]);
            toggleButton.setActionCommand(colorLabels[i].name());
            colorLabelSelection.add(toggleButton);
            constraint = constrain(2 + i, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE);
            editorSheet.add(toggleButton, constraint);
        }

        constraint = constrain(0, 6, labelCount, 1, 0.0d, 1.0d, GridBagConstraints.CENTER, GridBagConstraints.VERTICAL);
        editorSheet.add(Box.createVerticalGlue(), constraint);
    }
}